/*
 * edulisp_interpreter.cpp
 *
 *  Created on: 26.04.2011
 *      Author: Дворников А.А.
 *              Миночкина Н.О.
 *              Моисеев А.И.
 *
 *      Revision: 292
 */

#include "edulisp_interpreter.hpp"

Interpreter::Interpreter(istream& Input, ostream& Output)
	: p_Input(&Input), p_Output(&Output) { }

//Состояние интерпретатора
enum InterpreterState { IS_NORMAL, IS_USERFUNC, IS_USERFUNC_ARG,
	IS_USERFUNC_CODE, IS_IF_COND, IS_IF_TRUE, IS_IF_FALSE, IS_IF_COND_CALC };

//Метод принимает на вход таблицу имен, описатель функции в промежуточной форме,
//локальное пространство имен и переменную, куда поместить результат выполнения
//функции
InterpreterErrorCode Interpreter::Execute(NameTable& NameTable_,
		FunctionDescriptor& FunctionDescriptor_, LocalWorkspace *LocalWorkspace_,
		NameTable_Entry*& Result)
{
	//Формат списка аргументов:
	//0 FUNCTION_NAME1 ARG1 ARG2 .. ARGN 0 FUNCTION_NAME 2 ... END
	//Состояние интерпретатора
	InterpreterState State = IS_NORMAL;
	NameTable_Entry *UserFunction;
	//Указатель на описатель функции
	FunctionDescriptor *UserFunctionDescriptor = NULL;
	//Указатель на локальный результат
	NameTable_Entry *LocalResult;
	//Счётчик скобок
	unsigned int BracketCounter = 0;
	//Пока код никаких значений не вернул
	Result = NULL;
	//Флаг для lambda
	bool bLambda = false;
	//Ссылка на поток лексем
	LexemeList& LL = FunctionDescriptor_.GetLexemeList();
	//Текущий символ в потоке лексем
	unsigned int i = 0;
	//Делаем блок обработки исключений
	try
	{
		//Цикл интерпретации
		while (i < FunctionDescriptor_.GetLexemeList().size())
		{
			//Текущая лексема
			Lexeme L = LL[i];
			//Если это идентификатор
			if (L >= 0)
			{
				switch(State)
				{
				case IS_NORMAL: case IS_IF_COND_CALC: case IS_IF_TRUE:
						//Если есть карта локальных переменных
						if (LocalWorkspace_)
							Arguments.push_back(LocalWorkspace_->GetLocal(L));
						else
							//Если не объявлена, то тоже добавляем аргумент из таблицы лексем
							Arguments.push_back(NameTable_[L]);
					break;
				case IS_USERFUNC_ARG:
					//Добавляем аргумент в описатель функции
					UserFunctionDescriptor->GetArgumentList()
						->push_back(L);
					break;
				case IS_USERFUNC_CODE:
					//Добавляем код в описатель функции
					UserFunctionDescriptor->GetLexemeList().push_back(L);
					//Проверяем, является ли эта переменная локальной и
					//добавляем её в шаблон локальной карты имён
					UserFunctionDescriptor->CheckAndAddLocalVariable(L, NameTable_);
					break;
				default:
					break;
				}
			}
			//Если это оператор
			else
			{
				switch(State)
				{
				case IS_NORMAL: case IS_IF_COND_CALC: case IS_IF_TRUE:
					//if (bWaitForFunction && (-L != SSMO_LAMBDA) &&
							//(-L != SSMO_BRACKET_CLOSING) && (-L != SSMO_BRACKET))
						//Оператор не может быть именем функции
						//throw IEC_NOTFUNC;

					switch(-L)
					{
					case SSMO_BRACKET:
						//Разделитель списка аргументов
						Arguments.push_back(NULL);
						break;
					case SSMO_BRACKET_CLOSING:
							//Если пришла закрывающая скобка, пора выполнять верхнюю функцию
							//в стеке
							if (!Arguments.empty())
							{
								//Пока результата нет
								LocalResult = NULL;
								//Количество аргументов для встроенной функции
								unsigned int nArgNum = 0;

								//... FUNCTION_NAME ARG1 ARG2 ... END
								//    ArgPack		a
								//!ArgPack изменять нельзя!
								//Возможно, стоит сделать константным
								ArgumentList::iterator ArgPack, a;
								//Ищем начало последнего списка аргументов
								Arguments.FindSublist(ArgPack, nArgNum);

								//Если до этого была открывающая скобка
								if (-LL[i - 1] == SSMO_BRACKET)
								{
									//То это (), вернём NULL
									LocalResult = new NameTable_Entry(NULL, LT_RESULT);
								}
								else
								{
									//Перемещаем a на первый аргумент
									a = ArgPack; a++;
									NameTable_Entry *Function = *ArgPack;
									//Если вместо функции - константа, то ошибка
									if (Function->GetLexemeType() == LT_CONST)
									{
										throw IEC_CONST;
									}
									//Если это встроенная функция
									if (Function->GetDataType() != DT_USERFUNCTION)
									{
										InterpreterErrorCode E_Result
											= ExecuteInternal(NameTable_, nArgNum, Arguments, ArgPack, LocalResult);
										if (E_Result != IEC_OK)
											throw E_Result;
										LocalResult = PassResult(LocalResult);
										//clog << LocalResult << endl;
									}
									else
									{
										//Если это пользовательская функция
										InterpreterErrorCode E_Result
											= CallFunction(NameTable_, *(Function->GetFunctionDescriptor()),
													nArgNum, Arguments, ArgPack, LocalResult);
										if (E_Result != IEC_OK)
											throw E_Result;
										LocalResult = PassResult(LocalResult);
									}
								}

								//Если остались необработанные аргументы - убираем
								Arguments.ClearSublist(ArgPack);

								//Если это условие оператора ветвления
								if (State == IS_IF_COND_CALC)
								{
									if(!LocalResult)
										throw IEC_NORESULT;

									//Если не ноль
									if (!(LocalResult->ZeroValue()))
									{
										State = IS_IF_TRUE;
										i += 2;
									}
									else
									{
										State = IS_NORMAL;
										i += -LL[i + 1];
										++i;
									}
									//Если результат временный - удаляем
									if (LocalResult->GetLexemeType() == LT_RESULT)
										delete LocalResult;

									continue;
								}
								//Если функция вернула значение, то добавляем в список
								//аргументов
								if (LocalResult)
								{
									//Если есть локальная карта имён, то запросить
									//возвращаемое выражение
									if (LocalWorkspace_)
										Arguments.push_back(LocalWorkspace_->Return(LocalResult));
									else
										Arguments.push_back(LocalResult);
								}

								//Если это код первого случая оператора ветвления
								if (State == IS_IF_TRUE)
								{
									State = IS_NORMAL;
									i += -LL[i + 1];
									++i;
									continue;
								}
							}
						break;
					case SSMO_USERFUNC: case SSMO_LAMBDA:
						bLambda = (-L == SSMO_LAMBDA);
						//Присвоим пользовательскую функцию в переменную для удобства
						UserFunction = NameTable_[LL[++i]];
						UserFunctionDescriptor = UserFunction->CreateFunctionDescriptor();
						UserFunctionDescriptor->SetFunctionID(LL[i]);
						//Если далее следует не оператор, то создадим список аргументов
						if (NameTable_[LL[i + 1]]->GetLexemeType() != LT_OP)
							UserFunctionDescriptor->CreateArgumentList();

						State = IS_USERFUNC_ARG;
						break;
					case SSMO_IF:
						State = IS_IF_COND;
						//Если вычисляемое выражение
						if (LL[i + 1] < 0)
							State = IS_IF_COND_CALC;
						else
						{
							//Иначе обработаем здесь
							if (!(NameTable_[LL[++i]]->ZeroValue()))
							{
								State = IS_IF_TRUE;
								i += 2;
							}
							else
							{
								State = IS_NORMAL;
								i += -LL[i + 1];
								++i;
							}
							continue;
						}
						break;
					default:
						throw IEC_INTERNAL;
					}
					break;
				case IS_USERFUNC_ARG:
					switch(-L)
					{
					case SSMO_BRACKET_CLOSING:
						State = IS_USERFUNC_CODE;
						BracketCounter = 1;
						break;
					default:
						throw IEC_NOTID;
					}
					break;
				case IS_USERFUNC_CODE:
					switch(-L)
					{
					case SSMO_BRACKET_CLOSING:
						if (!(--BracketCounter))
						{
							//Если lambda, то вернём функцию
							if (bLambda)
							{
								UserFunctionDescriptor->GetLexemeList().push_back(L);
								Arguments.push_back(UserFunction);
							}

							State = IS_NORMAL;
						}
						else
							UserFunctionDescriptor->GetLexemeList().push_back(L);
						break;
					case SSMO_USERFUNC:
						throw IEC_INTDEFUN;
						break;
					case SSMO_BRACKET: case SSMO_LAMBDA:
						BracketCounter++;
						//
					default:
						UserFunctionDescriptor->GetLexemeList().push_back(L);
						break;
					}
					break;
				default:
					break;
				}
			}

			i++;
		}

		//Если остались результаты, то вернём первый
		if (!(Arguments.empty()))
		{
			//ArgumentList::iterator FirstResult;
			//unsigned int ResultNum;
			//Ищем начало последнего списка аргументов
			//Arguments.FindSublist(FirstResult, ResultNum);
			//cout << endl;
			//for (ArgumentList::iterator i = Arguments.begin(); i != Arguments.end(); i++)
				//cout << *i << ' ';
			ArgumentList::iterator LastResult = Arguments.end(); LastResult--;
			Result = *LastResult;
			//cout << Result << endl;
			//cout << endl;
		}
	}
	//Обработка исключений
	catch(InterpreterErrorCode ErrorCode)
	{
		return ErrorCode;
	}

	return IEC_OK;
}

//Удаляет переменные, предназначавшиеся отработанной функции, из списка
//и удаляет временные результаты LT_RESULT из памяти, а также переводит переменные
//LT_RESULT_PASS в LT_RESULT, либо удаляет их, если указан флаг ClearAll
void ArgumentList::ClearSublist(ArgumentList::iterator SublistHead, bool ClearAll)
{
	while(SublistHead != end())
	{
		if ((*SublistHead))
		{
			switch((*SublistHead)->GetLexemeType())
			{
			case LT_RESULT:
				delete *SublistHead;
				break;
			case LT_RESULT_PASS:
				if (ClearAll)
					delete *SublistHead;
				else
					(*SublistHead)->SetLexemeType(LT_RESULT);
				break;
			default:
				break;
			}
		}
		//*(p_Output) << *SublistHead << ' ';
		SublistHead = erase(SublistHead);
	}
	//cout << endl;
}

//Если переменная временная, то отмечает, что с ней может работать функция,
//находящаяся ниже по вложенности, иначе просто возвращает указатель на запись
NameTable_Entry* Interpreter::PassResult(NameTable_Entry *var)
{
	if (!var)
		return NULL;

	if (var->GetLexemeType() == LT_RESULT)
		var->SetLexemeType(LT_RESULT_PASS);

	return var;
}

//Выполнение математической операции с автоматическим выбором типа
bool Interpreter::MathOperation(int& X1, double& X2, NameTable_Entry& val,
		bool& Float, const char MathOperation)
{
	switch(MathOperation)
	{
	case '*':
		if (val.ZeroValue())
		{
			Float = false;
			X1 = 0;
			return false;
		}
		break;
	case '/':
		if (val.ZeroValue())
			throw IEC_DIVBYZERO;
		break;
	default:
		break;
	}
	if (Float)
		switch(MathOperation)
		{
		case '+':
			X2 += (val.GetDataType() == DT_INT) ? *(val.GetInteger())
				: *(val.GetFloat());
			break;
		case '-':
			X2 -= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
				: *(val.GetFloat());
			break;
		case '*':
			X2 *= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
				: *(val.GetFloat());
			break;
		case '/':
			X2 /= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
				: *(val.GetFloat());
			break;
		default:
			break;
		}
	else
		switch(MathOperation)
		{
		case '+':
			if (val.GetDataType() == DT_INT)
				X1 += (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
			else
			{
				X2 = static_cast<double>(X1);
				X2 += (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
				Float = true;
			}
			break;
		case '-':
			if (val.GetDataType() == DT_INT)
				X1 -= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
			else
			{
				X2 = static_cast<double>(X1);
				X2 -= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
				Float = true;
			}
			break;
		case '*':
			if (val.GetDataType() == DT_INT)
				X1 *= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
			else
			{
				X2 = static_cast<double>(X1);
				X2 *= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
				Float = true;
			}
			break;
		case '/':
			if (val.GetDataType() == DT_INT)
				X1 /= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
			else
			{
				X2 = static_cast<double>(X1);
				X2 /= (val.GetDataType() == DT_INT) ? *(val.GetInteger())
						: *(val.GetFloat());
				Float = true;
			}
			break;
		default:
			break;
		}

	return true;
}

//Инициализация математической операции с автоматическим выбором типа
bool Interpreter::InitMath(int& X1, double& X2, NameTable_Entry& val, bool& Float,
		const char MathOperator)
{
	//clog << "Init val: " << val << endl;
	switch(MathOperator)
	{
	case '/':
		if ((val.ZeroValue()))
		{
			Float = false;
			X1 = 0;
			return false;
		}
		Float = true;
		X2 = (val.GetDataType() == DT_INT) ? *(val.GetInteger())
			: *(val.GetFloat());
		break;
	case '*':
		if (val.ZeroValue())
		{
			Float = false;
			X1 = 0;
			return false;
		}
		break;
	default:
		break;
	}
	if ((Float = (val.GetDataType() != DT_INT)))
		X2 = *(val.GetFloat());
	else
		X1 = *(val.GetInteger());

	if (MathOperator == '/')
		Float = true;

	return true;
}

//Перевод итератора на последний список аргументов и удаление разделителя
void ArgumentList::FindSublist(ArgumentList::iterator& SublistHead,
			unsigned int& ArgumentsCounter)
{
	//for(ArgumentList::iterator i = Arguments.begin(); i != Arguments.end(); i++)
		//(*p_Output) << *i << ' ';
	//(*p_Output) << " -> ";
	if (!empty())
		for((SublistHead = end())--, ArgumentsCounter = 0;
				*SublistHead;
				SublistHead--)
			ArgumentsCounter++;
	if (!(*SublistHead))
		SublistHead = erase(SublistHead);
	//cout << *(SublistHead);
	//(*p_Output) << ", ";
}

//Отчистка списка аргументов с удалением всех временных переменных
void ArgumentList::ClearArguments()
{
	if (!empty())
		ClearSublist(begin(), true);
}

Interpreter::~Interpreter()
{

}

//Вызов функции, заполнение карты локальных переменных
InterpreterErrorCode Interpreter::CallFunction(NameTable& NameTable_,
		FunctionDescriptor& FunctionDescriptor_,
		unsigned int ArgumentsNumber, ArgumentList& Arguments,
		ArgumentList::iterator ArgumentsHead,
		NameTable_Entry*& Result)
{
	ArgumentDescriptor *UserFunctionArguments =
		FunctionDescriptor_.GetArgumentList();
	ArgumentList::iterator a = ArgumentsHead; a++;
	LocalWorkspace LocalWorkspace_(NameTable_,
			FunctionDescriptor_.GetLocalVariables(),
			FunctionDescriptor_.GetFunctionID());
	//LocalWorkspace_.BuildWorkspace(FunctionDescriptor_.GetLexemeList());
	//clog << "User function call, arguments ( ";
	if (UserFunctionArguments)
	{
		if (UserFunctionArguments->size()
				!= (ArgumentsNumber - 1))
			return IEC_INVARGS;
		for(ArgumentDescriptor::iterator i = UserFunctionArguments->begin();
				i != UserFunctionArguments->end(); i++, a++)
		{
			//NameTable_Entry *E =
			LocalWorkspace_.SetLocal(*i, *a);
			/*if (E)
				clog << *E;
			else
				clog << "NOT LOCAL";
			clog << ' ';*/
		}
			//(*i)->CopyData(**a);
	}
	//clog << ')' << endl;
	return Execute(NameTable_, FunctionDescriptor_,
			(UserFunctionArguments) ? &LocalWorkspace_ : NULL, Result);
}

//Обработка встроенной в интерпретатор функции
InterpreterErrorCode Interpreter::ExecuteInternal(NameTable& NameTable_,
				unsigned int ArgumentsNumber, ArgumentList& Arguments,
				ArgumentList::iterator ArgumentsHead, NameTable_Entry*& Result)
{
	//То корректируем количество аргументов,
	//вычитая лексему с именем функции
	ArgumentsNumber--;
	NameTable_Entry *Function = *ArgumentsHead;
	ArgumentList::iterator a = ArgumentsHead; a++;
	//Получаем указатель на имя
	string *FunctionName = Function->GetText();
	NameTable_Entry *var;
	//Флаг распознанной функции
	bool bParsed = false;
	Result = NULL;
	//Если имя есть
	if (FunctionName)
	{
		if (*FunctionName == "print")
		{
			//Если аргументов нет, то ошибка
			if (a == Arguments.end())
				throw IEC_NOARGS;
			while(a != Arguments.end())
			{
				var = *(a++);
				(*p_Output) << *var;
			}

			//Возвращаем true
			Result = new NameTable_Entry(NULL, LT_RESULT, true);
			bParsed = true;
		}
		if (*FunctionName == "setq")
		{
			var = *a++;
			NameTable_Entry *val = *(a);
			if (a == Arguments.end())
				return IEC_NOARGS;
			if (var->GetLexemeType() == LT_CONST)
				return IEC_CONST;
			if (val->GetDataType() == DT_NULL)
				return IEC_NOTDEFINED;

			var->CopyData(*val);

			Result = PassResult(val);
			bParsed = true;
		}
		if (*FunctionName == "progn")
		{
			if (a == Arguments.end())
				return IEC_NOARGS;

			Result = PassResult(*a);
			bParsed = true;
		}
		if ((*FunctionName == "+") || (*FunctionName == "-")
				|| (*FunctionName == "*")
				|| (*FunctionName == "/"))
		{
			int X1 = 0;
			double X2;
			bool bFloat = false;

			if (a == Arguments.end())
				return IEC_NOARGS;
			if (ArgumentsNumber < 2)
				return IEC_NOARGS;
			var = *(a++);
			if (InitMath(X1, X2, *var, bFloat, (*FunctionName)[0]))
			{
				while (a != Arguments.end())
				{
					var = *(a++);
					if (var->GetDataType() == DT_NULL)
						return IEC_NOTDEFINED;
					if ((var->GetDataType() != DT_INT) &&
							(var->GetDataType() != DT_DOUBLE))
						return IEC_INVTYPE;

					if (!MathOperation(X1, X2, *var, bFloat,
									(*FunctionName)[0]))
						break;
				}
			}

			Result = (bFloat) ?
					new NameTable_Entry(NULL, LT_RESULT, X2) :
					new NameTable_Entry(NULL, LT_RESULT, X1);
			bParsed = true;
		}
		if ((*FunctionName == "=") || (*FunctionName == "<="))
		{
			var = *a++;
			NameTable_Entry *var2 = *(a);
			if (a == Arguments.end())
				return IEC_NOARGS;

			bool b;
			if (*FunctionName == "=")
				b = *var == *var2;
			else
				b = *var <= *var2;
			//clog << *var << " == " << *var2 << ' ' << b << endl;
			Result = new NameTable_Entry(NULL, LT_RESULT,
					b);
			bParsed = true;
		}
		if (*FunctionName == "promt")
		{
			NameTable_Entry *greeting;
			if (a == Arguments.end())
				return IEC_NOARGS;
			while (a != Arguments.end())
			{
				greeting = *(a++);
				if (a == Arguments.end())
					var = new NameTable_Entry(NULL, LT_RESULT);
				else
					var = *(a++);
				(*p_Output) << *greeting << ": ";
				(*p_Input) >> *var;
			}

			Result = var;
			bParsed = true;
		}

		if (!bParsed)
			//Функцию распознать не удалось
			return IEC_UNKNOWNFUNC;
	}
	else
		Result = PassResult(Function);

	return (bParsed) ? IEC_OK : IEC_UNKNOWNFUNC;
}

LocalWorkspace::LocalWorkspace(NameTable& NameTable__, Lexeme Owner)
	: Owner_(Owner), NameTable_(NameTable__) { }

LocalWorkspace::~LocalWorkspace()
{ }

//Построение локальных переменных по потоку лексем
bool LocalWorkspace::BuildWorkspace(LexemeList& LexemeList_)
{
	Lexeme L;
	bool bLocalVariables = true;
	//clog << "Building workspace: ";
	for(unsigned int i = 0; i < (LexemeList_.size() - 1); i++)
	{
		L = LexemeList_[i];
		if ((L >= 0) && (NameTable_[L]->GetOwner() == Owner_) &&
				(LocalVariables_.find(L) == LocalVariables_.end()))
		{
			//clog << L << ' ';
			LocalVariables_.insert(LocalVariable(L,
					new NameTable_Entry(*NameTable_[L])));
			bLocalVariables = false;
		}
	}
	//clog << endl;

	return bLocalVariables;
}

//Возвращает локальную переменную, если она есть в карте локальных имён,
//иначе возвращает глобальную переменную из таблицы имён
NameTable_Entry *LocalWorkspace::GetLocal(Lexeme GlobalVariable)
{
	LocalVariables::iterator i;
	if ((i = LocalVariables_.find(GlobalVariable)) != LocalVariables_.end())
	{
		//clog << "Local access: " << *(i->second) << endl;
		return i->second;
	}
	else
	{
		//clog << "Global access: " << *NameTable_[GlobalVariable] << endl;
		return NameTable_[GlobalVariable];
	}
}

//Если локальная переменная, то возращает её копию для передачи в качестве
//результата, иначе - исходный указатель
NameTable_Entry *LocalWorkspace::Return(NameTable_Entry *LocalVariable)
{
	if (LocalVariable->GetOwner() == Owner_)
	{
		NameTable_Entry* Entry = new NameTable_Entry(*LocalVariable);
		Entry->SetLexemeType(LT_RESULT);
		return Entry;
	}
	else
		return LocalVariable;
}

//Копирует данные из Value в локальную переменную LocalVariable
NameTable_Entry *LocalWorkspace::SetLocal(Lexeme LocalVariable,
		NameTable_Entry *Value)
{
	LocalVariables::iterator i;
	//clog << "Trying to set local variable " << LocalVariable << "... ";
	if ((i = LocalVariables_.find(LocalVariable)) != LocalVariables_.end())
	{
		//clog << "founded." << endl;
		i->second->CopyData(*Value, false);
		return i->second;
	}
	else
	{
		//clog << "not founded." << endl;
		return NULL;
	}
}

//Конструктор из шаблонной карты локальных имён
LocalWorkspace::LocalWorkspace(NameTable& NameTable__, LocalVariables& Template,
		Lexeme Owner)
	: Owner_(Owner), NameTable_(NameTable__)
{
	for(LocalVariables::iterator i = Template.begin(); i != Template.end(); i++)
		LocalVariables_.insert(LocalVariable(i->first,
				new NameTable_Entry(*(i->second))));
}

ArgumentList::~ArgumentList()
{
	ClearSublist(begin(), true);
}
